/* -----------------------------------------------------------------------------
// Batch Gas Fit -- UV
//            Version 27.08.2012
// 
// Intended to be paired with a sequence of scripts:
//   -Offset + Dark CurrentFileNumber
//   -Sun (Kurusz) or Lamp Calibration
//   -Possibly gas concentration fits.
//   
//   ^^^These scripts are then intended to be run individually, or
//       sequentially using a DOS batch file
//       
//       If run sequentially, the date + spectrometer must be changed in *EACH* script
// 
//       What this script needs:
//           Line Spectra file (eg. HgNe)   
//           Reference Cross Sections (See below for list -- eg. "CrossSection_NO2_RawFile")
//                  -Cross sections should NOT be convolved already.
//           Calibrated Files (solar or lamp)
//           
// ------------------------------------------------------------------------------- 
// 
// by Eric Gentry, based on measurement scripts from Nicole Bobrowski + Leif Vogel (SO2)
// questions: Tilman.Hueneke@iup.uni-heidelberg.de
//            egentry@mit.edu
//            
// ------------------------------------------------------------------------------*/
    
    import System
    import System.IO
    import System.Threading
    import DoasCore
    import DoasCore.Spectra
    import DoasCore.IO
    import DoasCore.Device
    import DoasCore.Script
    import DoasCore.HMI
    import DoasCore.Math

// ========== UPDATE DATA IN THESE FILES! ==========
   
    //Below files must be in same folder as this script.
   var FolderLocationsFile   = "INFO_FolderLocations.txt"             
   var RunInputFile          = "INFO_DataProcessDirectory.txt"
   var SpectrometerInputFile = "INFO_SpectrometerProperties.txt"    //upper + lower boundary of convolution kernel  
   var GasInputFile          = "INFO_GasProperties_UV.txt"

   var bSingleRun            = false     //Only do one 1 spectrum? For debugging      
         
  // ========== Read-in User-define settings ==========     
  
  var FolderLocations        = FolderLocationsParse(FolderLocationsFile)    
  var ConvChannelObject      = SpectrometerParse(SpectrometerInputFile) 
  var GasInfoObject          = GasInfoParse(GasInputFile) 
  
  var BasePath               = FolderLocations.BasePath
  var CrossSectionBasePath   = FolderLocations.FitScenarios_CrossSections          
  
// ========= Files for trace gas evaluation
// Ref          - corrected + calibrated -- Should be collected data, e.g. mid-day at zenith
// TraceGasFit  - .fs file - It should have 9 gases. It does not matter which gases.              
//              - IF YOU CHANGE THE .fs FILE YOU MUST CHANGE THIS SCRIPT  (see documentation)
//                      - Almost all of the fit scenario is overwritten during processing.
//                      - That is done in case the fit scenario is [accidentally] damaged
//                      - If you *purposefully* chance the function, you will need to chance this script too.

   var KuruczFile                = CrossSectionBasePath + "neues Kurucz Spektrum 2010\\sao2010_solref_converted_intensity_vac.sp2";
   var TraceGasFitFile           = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\UV_master.fs";

   var CrossSection_NO2_RawFile  = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\no2_223K_bogumil_2003.dat";
   var CrossSection_O3_RawFile   = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\o3_223K_bogumil_2003.dat";
   var CrossSection_O4_RawFile   = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\O4_hermann.dat";
   var CrossSection_BrO_RawFile  = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\BrO_298K_Fleischmann_2004.dat";
   var CrossSection_HCHO_RawFile = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\HCHO_298K_MellerMoortgat_2000.dat";
   var CrossSection_OClO_RawFile = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\OClO_293K_Bogumil_2003.dat";   
   var CrossSection_IO_RawFile   = CrossSectionBasePath + "TACTS_FitScenarios_CrossSections\\IO_298K_Spietz_2005.dat";   

      
   //=======================Main Function (wrapper function)===========================
    function main()
    {
          var RunInfoArray_Temp = RunParse(RunInputFile)
          var RunInfoArray      = FilterInputArray(RunInfoArray_Temp)
   
          for(var index in RunInfoArray)
          {            
            Process(RunInfoArray[index])
            
            if(Script.StopAllScripts)
              {break}
          }
    
          Console.WriteLine("UV Gas Fit Script stopped")
    }
   // ===================================================
   //                    Processing Function
   // ===================================================
  
    function Process(ProcessingInfoObject)
    {
        var Date            = ProcessingInfoObject.Date
        var Spectrometer    = ProcessingInfoObject.Spectrometer
        var TraceGasName    = ProcessingInfoObject.TraceGasName
        var CalibStyle      = ProcessingInfoObject.CalibStyle
        var RefSpecName     = ProcessingInfoObject.RefSpecName
        
        var SpecBasePath    = BasePath + Date + "_" + FolderLocations.FlightFiles + "\\" + Spectrometer + "\\"; 
        var LineSpectraFile = BasePath + Date + "_" + FolderLocations.CalibrationFiles + "\\" + Spectrometer + "\\CalibFiles\\" + Spectrometer + "_LineSpectra.sp2";       // LineSpectra File  (Hg or otherwise)

        var ReferenceFile   = SpecBasePath + "spectra_corrected_calibrated" + CalibStyle + "\\" + RefSpecName;
      
    
/*      //Examples: DO NOT UNCOMMENT
//         ProcessingInfoObject.Date          = "2012_06_16";     //MAKE SURE TO CHANGE REF SPEC!!!
//         ProcessingInfoObject.Spectrometer  = "UV1";          
//         ProcessingInfoObject.TraceGasName  = "NO2"
//         ProcessingInfoObject.CalibStyle    = "_LAMP";         //Original input of "solar" or "lamp"
//         ProcessingInfoObject.RefSpecName   = "M123456.std";   //Must include ".std"
*/

      // ============ Checking + building Paths

    var SpectraPath = SpecBasePath + "spectra_corrected_calibrated" + CalibStyle;
    var di          = new DirectoryInfo(SpectraPath)
    if(!di.Exists)
      {Console.WriteLine("Run path does not exist!!!")
       Console.WriteLine(SpectraPath)
       return;
      }

    var OutputBasePath = SpecBasePath + "ScriptOutput\\" + CalibStyle + "Calib_Fits_Using_" + RefSpecName.split(".")[0] + "\\"

        di = new DirectoryInfo(OutputBasePath)
    if(!di.Exists)
       {di.Create()}
          
     //create log files
   var ResultFile       = OutputBasePath + Date + "_" + Spectrometer + "_" + TraceGasName + CalibStyle + ".txt";
   var ResultFileDoasis = OutputBasePath + Date + "_" + Spectrometer + "_" + TraceGasName + CalibStyle + "_long.txt";
   
   if(!bSingleRun) //Suppress textfile output for single files (console still writes result)
   {                                             
       var dispFileSystem    = new ActiveXObject("Scripting.FileSystemObject")
       var outfile           = dispFileSystem.OpenTextFile(ResultFile,2, true)
       outfile.WriteLine("StartTime\t" + "FileNumber\t" +"FitResult" + TraceGasName + "\tFitError" + TraceGasName + "\tFitChi2" + "\tSZA" + "\tElevationAngle") 
       
       var CheckAngle        = new Object()
       var outfileCollection = new Object()
       
           di = new DirectoryInfo(OutputBasePath + "GasConc_AnglesSort_" + TraceGasName + "\\")
       if(!di.Exists)
          {di.Create()}
   }



   // ========= AutoFile Spectrum Objects =========
   
   var SpectraPrefix        = "M";
   var SpectraSuffix        = ".std";
   var DigitCountInName     = 6   //Eg. M123456.std has DigitCountInName == 6
  
   var MeasSpec : ISpectrum = Specbar.GetSpectrum("Measurement Spectrum")
  
   var MeasFile : AutoFileName    = new AutoFileName()
       MeasFile.BasePath          = SpectraPath
       MeasFile.Prefix            = SpectraPrefix
       MeasFile.Suffix            = SpectraSuffix
       MeasFile.NumberOfDigits    = DigitCountInName
       MeasFile.Mode              = "Manual"; //necessary so skip the "\M000000\" folder in filepath
       MeasFile.FindFirstIndex() 

  var KuruczRawSpec : ISpectrum = Specbar.GetSpectrum("KuruczRawSpec")
  var LineSpec      : ISpectrum = Specbar.GetSpectrum("LineSpec")
  var RefSpec       : ISpectrum = Specbar.GetSpectrum("RefSpec")
  var RingSpec      : ISpectrum = Specbar.GetSpectrum("RingSpec")
  var NO2RawSpec    : ISpectrum = Specbar.GetSpectrum("NO2_Raw")
  var O3RawSpec     : ISpectrum = Specbar.GetSpectrum("O3_Raw")
  var O4RawSpec     : ISpectrum = Specbar.GetSpectrum("O4_Raw")
  var BrORawSpec    : ISpectrum = Specbar.GetSpectrum("BrO_Raw")
  var HCHORawSpec   : ISpectrum = Specbar.GetSpectrum("HCHO_Raw")
  var OClORawSpec   : ISpectrum = Specbar.GetSpectrum("OClO_Raw")
  var IORawSpec     : ISpectrum = Specbar.GetSpectrum("IO_Raw")

  var NO2ConvSpec   : ISpectrum = Specbar.GetSpectrum("NO2_Conv")
  var O3ConvSpec    : ISpectrum = Specbar.GetSpectrum("O3_Conv")
  var O4ConvSpec    : ISpectrum = Specbar.GetSpectrum("O4_Conv")
  var BrOConvSpec   : ISpectrum = Specbar.GetSpectrum("BrO_Conv")
  var HCHOConvSpec  : ISpectrum = Specbar.GetSpectrum("HCHO_Conv")
  var OClOConvSpec  : ISpectrum = Specbar.GetSpectrum("OClO_Conv")
  var IOConvSpec    : ISpectrum = Specbar.GetSpectrum("IO_Conv")
    
                                                   
//Prepare spectra
   KuruczRawSpec.Open(KuruczFile)
         RefSpec.Open(ReferenceFile)
        LineSpec.Open(LineSpectraFile)   
      NO2RawSpec.Open(CrossSection_NO2_RawFile)    
       O3RawSpec.Open(CrossSection_O3_RawFile)
       O4RawSpec.Open(CrossSection_O4_RawFile)
      BrORawSpec.Open(CrossSection_BrO_RawFile)
     HCHORawSpec.Open(CrossSection_HCHO_RawFile)
     OClORawSpec.Open(CrossSection_OClO_RawFile)
       IORawSpec.Open(CrossSection_IO_RawFile)        
             
       //Give the convolution target spectrum the right channels + calibration
       //NOTE: THE ACTUAL SPECTRAL DATA BELOW IS CLEARED.
       //ONLY NUMBER OF CHANNELS + WAVELENGTHS REMAIN.
     NO2ConvSpec.Open(CrossSection_NO2_RawFile)   
      O3ConvSpec.Open(CrossSection_O3_RawFile)
      O4ConvSpec.Open(CrossSection_O4_RawFile)
     BrOConvSpec.Open(CrossSection_BrO_RawFile)
    HCHOConvSpec.Open(CrossSection_HCHO_RawFile)
    OClOConvSpec.Open(CrossSection_OClO_RawFile)
      IOConvSpec.Open(CrossSection_IO_RawFile)    
      
      Scattering.CalcRingSpectrum(RefSpec).Copy(RingSpec)
        SpecMath.Log(RingSpec)
        SpecMath.Log(RefSpec)  
        SpecMath.SubScalar(LineSpec,LineSpec.Min)  //subtract the minimum of intensity in the math range - re-zeros the math range
              
//Convolve cross sections
     LineSpec.EnableMathMarker = true;
     LineSpec.MathLow          = ConvChannelObject[Spectrometer].Low;
     LineSpec.MathHigh         = ConvChannelObject[Spectrometer].High;
     
    	DoasFit.GaussFit(LineSpec)  //set marker to peak
    	
    	 
    
// convolve -- COMMENT OUT ALL BUT 1 CONVOLUTION FOR EACH GAS!!
     //Convolution syntax: ConvoluteI0SaturationCorrected(HighResSpec, SlitFunctionShape, SampleOntoTheseChannels, HighResSolarSpec, EstimatedColumnDensity, options)   
    //if-else structure: if you input ApproxConc = 0, then you want a convolution WITHOUT I0 correction
    //      -Only need I0 correction for STRONG absorbers
    
    //NO2 Conv     
  	    if(GasInfoObject["NO2"].ApproxConc == 0)
           SpecMath.Convolute(NO2RawSpec, LineSpec, NO2ConvSpec, KernelPrepMode.Normalize)
        else
           SpecMath.ConvoluteI0SaturationCorrected(NO2RawSpec, LineSpec, NO2ConvSpec, KuruczRawSpec, GasInfoObject["NO2"].ApproxConc, KernelPrepMode.Normalize)
    
    //O3 Conv      
        if(GasInfoObject["O3"].ApproxConc == 0)
           SpecMath.Convolute(O3RawSpec,  LineSpec, O3ConvSpec,  KernelPrepMode.Normalize)
        else   
           SpecMath.ConvoluteI0SaturationCorrected(O3RawSpec,  LineSpec, O3ConvSpec,  KuruczRawSpec, GasInfoObject["O3"].ApproxConc,  KernelPrepMode.Normalize) 
   
    //O4 Conv       
        if(GasInfoObject["O4"].ApproxConc == 0)
            SpecMath.Convolute(O4RawSpec,  LineSpec, O4ConvSpec,  KernelPrepMode.Normalize)
        else
            SpecMath.ConvoluteI0SaturationCorrected(O4RawSpec,  LineSpec, O4ConvSpec,  KuruczRawSpec, GasInfoObject["O4"].ApproxConc,  KernelPrepMode.Normalize) 
  
    //BrO Conv      
        if(GasInfoObject["BrO"].ApproxConc == 0)
            SpecMath.Convolute(BrORawSpec,  LineSpec, BrOConvSpec,  KernelPrepMode.Normalize)
        else
            SpecMath.ConvoluteI0SaturationCorrected(BrORawSpec, LineSpec, BrOConvSpec, KuruczRawSpec, GasInfoObject["BrO"].ApproxConc,  KernelPrepMode.Normalize) 

    //HCHO Conv       
        if(GasInfoObject["HCHO"].ApproxConc == 0)
            SpecMath.Convolute(HCHORawSpec,  LineSpec, HCHOConvSpec,  KernelPrepMode.Normalize)
        else
            SpecMath.ConvoluteI0SaturationCorrected(HCHORawSpec, LineSpec, HCHOConvSpec, KuruczRawSpec, GasInfoObject["HCHO"].ApproxConc,  KernelPrepMode.Normalize) 

    //OClO Conv       
        if(GasInfoObject["OClO"].ApproxConc == 0)
            SpecMath.Convolute(OClORawSpec,  LineSpec, OClOConvSpec,  KernelPrepMode.Normalize)
        else
            SpecMath.ConvoluteI0SaturationCorrected(OClORawSpec, LineSpec, OClOConvSpec, KuruczRawSpec, GasInfoObject["OClO"].ApproxConc,  KernelPrepMode.Normalize) 
 
    //IO Conv     
  	    if(GasInfoObject["IO"].ApproxConc == 0)
           SpecMath.Convolute(IORawSpec, LineSpec, IOConvSpec, KernelPrepMode.Normalize)
        else
           SpecMath.ConvoluteI0SaturationCorrected(IORawSpec, LineSpec, IOConvSpec, KuruczRawSpec, GasInfoObject["IO"].ApproxConc, KernelPrepMode.Normalize)



     //Preparing the Fit Scenario---------------------------
    var TraceGasFit :DoasFit = new DoasFit()
                 TraceGasFit = DoasFit.Open(TraceGasFitFile)   
      
              //Which index (in the fit scenario) does each gas match?
    var FitScenarioIndexObject = {"NO2"  : 2,
                                  "O3"   : 3,
                                  "O4"   : 4,
                                  "BrO"  : 5,
                                  "HCHO" : 6,
                                  "OClO" : 7,
                                  "IO"   : 8}


         RefSpec.Copy(TraceGasFit.ReferencesInfo[0].ReferenceSpectrum)
        RingSpec.Copy(TraceGasFit.ReferencesInfo[1].ReferenceSpectrum)
     NO2ConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["NO2"]] .ReferenceSpectrum)
      O3ConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["O3"]]  .ReferenceSpectrum)
      O4ConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["O4"]]  .ReferenceSpectrum) 
     BrOConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["BrO"]] .ReferenceSpectrum)
    HCHOConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["HCHO"]].ReferenceSpectrum)
    OClOConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["OClO"]].ReferenceSpectrum)
      IOConvSpec.Copy(TraceGasFit.ReferencesInfo[FitScenarioIndexObject["IO"]]  .ReferenceSpectrum)              
          
        //Add the correct spectra identifier ("TraceGasName") -- necessary for linking parameters
        TraceGasFit.ReferencesInfo[0].ObjectKey = "RefSpec";
        TraceGasFit.ReferencesInfo[1].ObjectKey = "RingSpec";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["NO2"]] .ObjectKey = "NO2";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["O3"]]  .ObjectKey = "O3";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["O4"]]  .ObjectKey = "O4";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["BrO"]] .ObjectKey = "BrO";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["HCHO"]].ObjectKey = "HCHO";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["OClO"]].ObjectKey = "OClO";
        TraceGasFit.ReferencesInfo[FitScenarioIndexObject["IO"]]  .ObjectKey = "IO";
        
     var FileNumberCounter :int = MeasFile.CurrentFileNumber
     //^^Because printing MeasFile.CurrentFileNumber gives you an off-by-one error on the last spectrum

     while(!Script.StopAllScripts && MeasFile.Open(MeasSpec))
     {
      //Select fit window
      var CalibPoly = MeasSpec.CalibPolynomial
         TraceGasFit.FitRanges[0].LimitLow  = ConvertWavelengthToChannelNumber(GasInfoObject[TraceGasName].WavelengthRange.Low,  CalibPoly)
         TraceGasFit.FitRanges[0].LimitHigh = ConvertWavelengthToChannelNumber(GasInfoObject[TraceGasName].WavelengthRange.High, CalibPoly)
       
                

      if(TraceGasFit.DoFit(MeasSpec))
          {     
          //Parse information to write to logfile
          var TraceGas       = TraceGasFit.ReferencesInfo[FitScenarioIndexObject[TraceGasName]].FitCoefficient;
          var TraceGasE      = TraceGasFit.ReferencesInfo[FitScenarioIndexObject[TraceGasName]].FitCoefficientError;
          var Chi2           = TraceGasFit.ChiSquare
          var SZA            = MeasSpec.ScanGeometry.SZA
          var ElevationAngle = MeasSpec.ElevationAngle 
          
          if(!bSingleRun)
              {
              TraceGasFit.AppendResultToFile(ResultFileDoasis)
              outfile.WriteLine(MeasSpec.StartTime + "\t" + FileNumberCounter + "\t"  + TraceGas.ToString() + "\t"   + TraceGasE.ToString() + "\t" + Chi2.ToString() + "\t" + SZA.ToString() + "\t" + ElevationAngle)
              SortElevationAngles(MeasSpec.StartTime, FileNumberCounter, TraceGas.ToString(), TraceGasE.ToString(), Chi2.ToString(), SZA.ToString(), ElevationAngle.ToString(), outfileCollection, CheckAngle, SpecBasePath, Spectrometer, Date, TraceGasName, OutputBasePath)
              }
          
          Console.WriteLine(MeasSpec.StartTime + "\t" + FileNumberCounter +  ":  "+ TraceGas.ToString() + " +- " + TraceGasE.ToString() + "  " + SZA.ToString() + "  " + ElevationAngle)

          
          FileNumberCounter++  //increase the count by 1 each loop, incase you haven't seen the "++" operator         
          }
         
          if(bSingleRun)
            {break}
      }
   
    if(!bSingleRun)
      {outfile.Close()
       WriteRefSpecProperties(RefSpec, OutputBasePath)}
    
    }
    
 //*****************************************************************************************//
   
    
    function CopyCalibration(CalibratedSpec, DestinationSpec)    
{
  DestinationSpec.CalibPolynomialOrder = CalibratedSpec.CalibPolynomialOrder;
  for(var iCount :int = 0; iCount <= CalibratedSpec.CalibPolynomialOrder; iCount++)
      {DestinationSpec.CalibPolynomial[iCount] = CalibratedSpec.CalibPolynomial[iCount]}
}

//*****************************************************************************************//

    
    function ConvertWavelengthToChannelNumber(Wavelength, CalibPoly)    
{ //ASSUMING A 2ND ORDER FIT       (of the form "(a * x^2) +  (b * x) + c = wavelength"  where x = channel number)
  var a = CalibPoly[2] 
  var b = CalibPoly[1] 
  var c = CalibPoly[0] - Wavelength  //Need to subtract "Wavelength" to use quadratic equation!! (set it equal to 0)
  
  var ChannelNum = 0
      ChannelNum = ((-1 * b) + Math.sqrt(Math.pow(b,2) - 4*a*c)) / (2 * a)
      
      //Filter edge effects and out-of-range channel values 
      //(eg 400nm on VIS would be a negative channel)
      if(ChannelNum < 5)
        {ChannelNum = 5}
      if(ChannelNum > 2040)
        {ChannelNum = 2040}
       
    //   Console.WriteLine(int(ChannelNum))
                                                                                    
  return int(ChannelNum)
   
}

 //*****************************************************************************************//  
    
    
function RunParse(InputFile)
//  Uses an ActiveX object to give JavaScript the ability to read a text file
//  First line is discarded (header information)
//  Further lines are space-delimited.
//  Returns an array of Objects
    {
     var ReadFileSystem = new ActiveXObject("Scripting.FileSystemObject")
     var infile         = ReadFileSystem.GetFile(InputFile)
     var infileStream   = infile.OpenAsTextStream(1, 0)

     var TemporaryArray   = new Array()
     var OutputArray      = new Array()
     var TempLine         = ""
   
     for(var count = 0; !infileStream.AtEndOfStream; count++)
     {
        TempLine = infileStream.ReadLine()
        
        if(count == 0)
           continue
        
        if(TempLine == "")
           continue
           
        else
          TemporaryArray.push(TempLine.split(" "))
     } 
     
     infileStream.Close()
     
     
     for(var index in TemporaryArray)
        {
          var RunInfoObject = {"Date"         : TemporaryArray[index][0],
                               "Spectrometer" : TemporaryArray[index][1].toUpperCase(),
                               "CalibStyle"   : SolarOrLampCalib(TemporaryArray[index][2]),
                               "RefSpecName"  : TemporaryArray[index][3],
                               "TraceGasName" : TemporaryArray[index][4]}
                               
          OutputArray.push(RunInfoObject)     
        }
 
        //Getting an "Object Expected" Error? Your input file doesn't have enough information!
     
     return OutputArray 
    }  
    
 //*****************************************************************************************//    
 
 function SpectrometerParse(InputFile)
//  Uses an ActiveX object to give JavaScript the ability to read a text file
//  First line is discarded (header information)
//  Further lines are space-delimited.
//  Returns an array of Objects
    {
     var ReadFileSystem = new ActiveXObject("Scripting.FileSystemObject")
     var infile         = ReadFileSystem.GetFile(InputFile)
     var infileStream   = infile.OpenAsTextStream(1, 0)

     var TemporaryArray   = new Array()
     var OutputObject     = new Object()
     var TempLine         = ""
   
     for(var count = 0; !infileStream.AtEndOfStream; count++)
     {
        TempLine = infileStream.ReadLine()
        
        if(count == 0)
           continue
        
        if(TempLine == "")       
           continue
           
        else
          TemporaryArray.push(TempLine.split(" "))
     } 
     
     infileStream.Close()
     
     
     for(var index in TemporaryArray)
        {        
          OutputObject[TemporaryArray[index][0]] = {"Low"  : TemporaryArray[index][1],
                                                    "High" : TemporaryArray[index][2]}  
        }

        //Getting an "Object Expected" Error? Your input file doesn't have enough information!  (or empty lines)

     
     return OutputObject 
} 
 
//*****************************************************************************************//  

 
 function GasInfoParse(InputFile)
//  Uses an ActiveX object to give JavaScript the ability to read a text file
//  First line is discarded (header information)
//  Further lines are space-delimited.
//  Returns an array of Objects
    {
     var ReadFileSystem   = new ActiveXObject("Scripting.FileSystemObject")
     var infile           = ReadFileSystem.GetFile(InputFile)
     var infileStream     = infile.OpenAsTextStream(1, 0)

     var TemporaryArray   = new Array()
     var OutputObject     = new Object()
     var TempLine         = ""

     for(var count = 0; !infileStream.AtEndOfStream; count++)
     {
        TempLine = infileStream.ReadLine()
        
        if(count == 0)
           continue
        
        if(TempLine == "")       
           continue
           
        else
          TemporaryArray.push(TempLine.split(" "))
     } 
     
     infileStream.Close()
     
     
     
     for(var index in TemporaryArray)
        {
          var TempWavelengthRangeObject = {"Low"  : parseFloat(TemporaryArray[index][1]),
                                           "High" : parseFloat(TemporaryArray[index][2])}
                                           
          OutputObject[TemporaryArray[index][0]] = {"WavelengthRange" : TempWavelengthRangeObject,
                                                    "ApproxConc"      : parseFloat(TemporaryArray[index][3])}
        }

        //Getting an "Object Expected" Error? Your input file doesn't have enough information! (or empty lines)
     
     return OutputObject 
} 
  
//*****************************************************************************************//  

  function SolarOrLampCalib(CalibStyleInput)
 {
  var CalibStyle = ""
  
  if(CalibStyleInput == "lamp")
      return "_LAMP"
             
  else if (CalibStyleInput == "sun" || CalibStyleInput == "solar")
      return "_SOLAR"
             
  else
    {Console.WriteLine("Error in SolarOrLampCalib() -- invalid input")
     Console.WriteLine("Input given: " + CalibStyleInput)
     Console.WriteLine("Valid input choices: 'solar', 'lamp'")
      Script.StopAllScripts = true
      return   
    }

 }
 
//*****************************************************************************************//  
    function FilterInputArray(InputArray)
     //===Check for repeated sets of dates + spectrometer pairs
    { 
     var OutputArray  = new Array()
     
     for(var LineNum in InputArray)
     {    
          //Only push UV spectrometers
          if(InputArray[LineNum].Spectrometer.indexOf("UV") != -1)
            OutputArray.push(InputArray[LineNum])
     }

     return OutputArray
    }
 
 //*****************************************************************************************//    
 
 function FolderLocationsParse(InputFile)
//  Uses an ActiveX object to give JavaScript the ability to read a text file
//  First line is discarded (header information)
//  Further lines are space-delimited.
//  Returns an array of Objects
    {
     var ReadFileSystem   = new ActiveXObject("Scripting.FileSystemObject")
     var infile           = ReadFileSystem.GetFile(InputFile)
     var infileStream     = infile.OpenAsTextStream(1, 0)

     var TemporaryArray   = new Array()
     var OutputObject     = new Object()
     var TempLine         = ""
   
     for(var count = 0; !infileStream.AtEndOfStream; count++)
     {  
        TempLine = infileStream.ReadLine();
           
        if(count == 0)
          continue
          
        if(TempLine == "")     //skip empty lines in textfile
          continue
            
        else
          TemporaryArray.push(TempLine.split("\"")[1])   //Use " as a delimiter
     } 
     
     infileStream.Close()    
     
     var BasePath                   =  TemporaryArray[0]
     var FitScenarios_CrossSections =  TemporaryArray[1]
     
     //Add at "\" to the end of the folder names, if it is missing
     if(BasePath[BasePath.length-1] != "\\")     
        BasePath = BasePath + "\\"
     
     if(FitScenarios_CrossSections[FitScenarios_CrossSections.length -1] != "//")
        FitScenarios_CrossSections = FitScenarios_CrossSections + "\\"
     
     //construct output
     OutputObject  = {"BasePath"                   : BasePath,
                      "FitScenarios_CrossSections" : FitScenarios_CrossSections,
                      "CalibrationFiles"           : TemporaryArray[2],
                      "FlightFiles"                : TemporaryArray[3]
                      }  
        
        //Getting an "Object Expected" Error? Your input file doesn't have enough information! (or empty lines)
     
     return OutputObject 
}  
    
//*****************************************************************************************//  

function SortElevationAngles(StartTime, FileNumberCounter, TraceGas, TraceGasE, Chi2, SZA, ElevationAngle, outfileCollection, CheckAngle, SpecBasePath, Spectrometer, Date, TraceGasName, OutputBasePath)
//Only used if: 'bSingleRun == false'
{
     ElevationAngle.replace(".", ",")
     
     if(CheckAngle[ElevationAngle] != true)
        {
         CheckAngle[ElevationAngle] = true   
         var logFileName                       = OutputBasePath + "GasConc_AnglesSort_" + TraceGasName + "\\" + ElevationAngle + "deg_" + Date + "_" + Spectrometer + "_" + TraceGasName + ".txt";
         var dispFileSystem                    = new ActiveXObject("Scripting.FileSystemObject")
         outfileCollection[ElevationAngle]     = dispFileSystem.OpenTextFile(logFileName,2, true)  
         //Set Column Headers for logfile
           outfileCollection[ElevationAngle].WriteLine("StartTime\t" + "FileNumber\t" +"FitResult" + TraceGasName + "\tFitError" + TraceGasName + "\tFitChi2" + "\tSZA" + "\tElevationAngle")   
        }
     
      outfileCollection[ElevationAngle].WriteLine(StartTime + "\t" + FileNumberCounter + "\t"  + TraceGas.ToString() + "\t"   + TraceGasE.ToString() + "\t" + Chi2.ToString() + "\t" + SZA.ToString() + "\t" + ElevationAngle)
}

    
//*****************************************************************************************//
  
function WriteRefSpecProperties(RefSpec, OutputBasePath)
// Uses spectrum object + .std file (read as text file) and writes a new logfile of important information
// 
// To change what the program saves in the logfile, look inside the while() loop or  
{
     var RefSpecFileName   = RefSpec.FileName
     var shortFileName     = RefSpecFileName.substring(RefSpecFileName.lastIndexOf("\\") +1).split(".")[0] // strip filename except "M00123"    
  
     var ReadFileSystem    = new ActiveXObject("Scripting.FileSystemObject")
     var infile            = ReadFileSystem.GetFile(RefSpecFileName)
     var infileStream      = infile.OpenAsTextStream(1, 0)
     
     var dispFileSystem    = new ActiveXObject("Scripting.FileSystemObject")
     var outfile           = dispFileSystem.OpenTextFile(OutputBasePath + "RefSpec_" + shortFileName + "_properties.txt",2, true)  
     
     var StartTimeShort    = RefSpec.StartTime
     var StartTimeMS       = ""
     var  StopTimeShort    = RefSpec.StopTime
     var  StopTimeMS       = ""
     
     //Add more properties here if you want to save them! Remember to write a outfile.WriteLine() statement below the while loop!
     var ElevationAngle    = RefSpec.ElevationAngle
     var Altitude          = RefSpec.Altitude
     var SZA               = RefSpec.ScanGeometry.SZA
     var Latitude          = RefSpec.ScanGeometry.Latitude
     var Longitude         = RefSpec.ScanGeometry.Longitude          
   
     SpecMath.Exp(RefSpec)                         //Remember that when you pass it RefSpec, it is still the log of the spectra! (due to fitting)
     var NumScans          = RefSpec.NumScans      //needed to calculate saturation
     var Max               = RefSpec.Max
     var SaturationPercent = Max / (NumScans * 65536) * 100
   
   
     var TempLine          = ""
   
     //If you want to change which values are printed, change the "if... else if..." structure below, or the RefSpec.Property lines above
   
     while(!infileStream.AtEndOfStream)
     {  
        TempLine = infileStream.ReadLine();
           
             if(TempLine == "")
                continue    //skip empty lines                             
                
        else if(TempLine.indexOf("Begin of scan ms") != -1)
			          StartTimeMS = TempLine.split("= 0")[1]
			           
	     	else if(TempLine.indexOf("End of scan ms") != -1)
			          StopTimeMS  = TempLine.split("= 0")[1]
                
        else if(TempLine.indexOf("Timezone ") != -1)
                outfile.WriteLine("Timezone:\t" + TempLine.split("\"")[1])                      
       
     }
     
     if(StartTimeMS != "")
          outfile.WriteLine("StartTime:\t" + StartTimeShort + StartTimeMS) 

     if(StopTimeMS != "")
          outfile.WriteLine("StopTime:\t"  + StopTimeShort  +  StopTimeMS)
     
     outfile.WriteLine("SaturationPercent:\t" + SaturationPercent)
     outfile.WriteLine("ElevationAngle:\t"    + ElevationAngle)
     outfile.WriteLine("NumScans:\t"          + NumScans)
     outfile.WriteLine("SZA:\t"               + SZA)
     outfile.WriteLine("Altitude:\t"          + Altitude)
     outfile.WriteLine("Longitude:\t"         + Longitude)
     outfile.WriteLine("Latitude:\t"          + Latitude)
     outfile.WriteLine("Max:\t"               + Max)
     outfile.WriteLine("NumScans:\t"          + NumScans)
     
     infileStream.Close()
          outfile.Close()    
}     
  //*****************************************************************************************//
  
    
    main();